#include <arena.h>
#include <assert.h>

namespace minidb
{

constexpr std::size_t KBlockSize = 4096;

Arena::Arena() :
    alloc_ptr_(nullptr),
    alloc_bytes_remaining_(0),
    memory_usage_(0)
{
}

char *Arena::Allocate(size_t bytes)
{
    assert(bytes > 0);
    if (bytes <= alloc_bytes_remaining_)
    {
        char *result = alloc_ptr_;
        alloc_ptr_ += bytes;
        alloc_bytes_remaining_ -= bytes;
        return result;
    }
    return AllocateFallback(bytes);
}

char *Arena::AllocateAligned(size_t bytes)
{
    const std::size_t align = (sizeof(void *)) > 8 ? sizeof(void *) : 8;
    // 判断对齐大小是2的n次幂
    static_assert((align & (align - 1)) == 0, "Pointer size should be power of 2.");
    // 计算对齐所需偏移量
    std::size_t current_mod = reinterpret_cast<intptr_t>(alloc_ptr_) & (align - 1);
    std::size_t slop = align - current_mod;
    std::size_t needed = bytes + slop;
    char *result;
    if (needed <= alloc_bytes_remaining_) // 保证align对齐
    {
        result = alloc_ptr_ + slop;
        alloc_bytes_remaining_ -= needed;
        alloc_ptr_ += needed;
    }
    else
    {
        result = AllocateFallback(bytes); // 由malloc保证align对齐
    }
    assert((reinterpret_cast<uintptr_t>(result) & (align - 1)) == 0);
    return result;
}

Arena::~Arena()
{
    for (std::size_t i = 0; i < blocks_.size(); ++i)
    {
        delete[] blocks_[i];
    }
}

char *Arena::AllocateNewBlock(std::size_t bytes)
{
    char *result = new char[bytes];
    blocks_.push_back(result);
    memory_usage_.fetch_add(bytes + sizeof(char *),
                            std::memory_order_relaxed);
    return result;
}

char *Arena::AllocateFallback(std::size_t bytes)
{
    /**
     * 大于KBlockSize/4, 直接分配bytes字节
     * 小于KBlockSize/4, 分配KBlockSize字节, 并更新alloc_ptr_和remaining_
     */
    if (bytes > KBlockSize / 4)
    {
        return AllocateNewBlock(bytes);
    }
    char *result = AllocateNewBlock(KBlockSize);
    alloc_bytes_remaining_ = KBlockSize - bytes;
    alloc_ptr_ = result + bytes;
    return result;
}

}; // namespace minidb